package com.server.questionbank.snframe.util.FileUtil;

import com.server.questionbank.snframe.util.FileUtil.common.util.ValidationUtils;
import com.server.questionbank.snframe.util.FileUtil.lifecycle.LifecycleConfigurationBuilder;
import com.server.questionbank.snframe.util.FileUtil.lifecycle.LifecycleManager;
import com.server.questionbank.snframe.util.FileUtil.lifecycle.model.LifecycleRule;
import com.server.questionbank.snframe.util.FileUtil.operation.command.DeleteCommand;
import com.server.questionbank.snframe.util.FileUtil.operation.command.DownloadCommand;
import com.server.questionbank.snframe.util.FileUtil.operation.multipart.MultipartDownloadOperation;
import com.server.questionbank.snframe.util.FileUtil.policy.BucketPolicy;
import com.server.questionbank.snframe.util.FileUtil.policy.PolicyType;
import com.server.questionbank.snframe.util.FileUtil.policy.factory.PolicyFactory;
import com.server.questionbank.snframe.util.FileUtil.common.util.ContentTypeUtils;
import com.server.questionbank.snframe.util.FileUtil.operation.command.UploadCommand;
import io.minio.*;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.util.*;

/**
 * MinIO操作门面类
 */
@Slf4j
@Component
public class MinIoUtil {

    @Resource
    private MinioClient minioClient;

    @Resource
    private LifecycleManager lifecycleManager;

    /**
     * 普通文件，大文件上传，支持自动分片
     */
    public ObjectWriteResponse uploadFile(String bucket, String object, MultipartFile file) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);

        return new UploadCommand(
            minioClient,
            bucket,
            object,
            file.getInputStream(),
            file.getSize(),
            ContentTypeUtils.getContentType(file.getOriginalFilename())
        ).execute();
    }

    // 上传Base64编码的文件
    public ObjectWriteResponse uploadBase64File(String bucket, String fileName, InputStream fileInputStream, Long fileSize, String contentType) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(fileName);

        return new UploadCommand(
                minioClient,          // MinioClient 实例
                bucket,           // 存储桶名称
                fileName,             // 文件名
                fileInputStream,      // 输入流
                fileSize,             // 文件大小
                contentType           // 文件类型
        ).execute();
    }

    /**
     * 普通文件下载
     */
    public void downloadFile(String bucket, String object, File targetFile) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);
        
        new DownloadCommand(minioClient, bucket, object, targetFile, false).execute();
    }

    /**
     * 大文件分片下载
     */
    public void downloadLargeFile(String bucket, String object, File targetFile, 
            int chunkSize) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);
        ValidationUtils.validateChunkSize(chunkSize);
        
        new MultipartDownloadOperation(
            minioClient, bucket, object, targetFile, chunkSize
        ).execute();
    }

    /**
     * 删除文件
     */
    public void deleteFile(String bucket, String object) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);
        
        new DeleteCommand(minioClient, bucket, object).execute();
    }

    /**
     * 检查存储桶是否存在
     */
    public boolean isBucket(String bucketName) {
        try {
            return minioClient.bucketExists(
                BucketExistsArgs.builder()
                    .bucket(bucketName)
                    .build()
            );
        } catch (Exception e) {
            log.error("检查桶是否存在时发生错误: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 创建存储桶并设置生命周期和策略
     *
     * @param bucket 存储桶名称
     * @param region 区域
     * @param expirationDays 过期天数，如果为null则不设置生命周期
     * @param policyType 存储桶策略类型，如果为null则不设置策略
     */
    public void createBucket(String bucket, String region, Integer expirationDays, PolicyType policyType) throws Exception {
        ValidationUtils.validateBucket(bucket);
        
        if (!isBucket(bucket)) {
            // 1. 创建存储桶
            minioClient.makeBucket(
                MakeBucketArgs.builder()
                    .bucket(bucket)
                    .region(region)
                    .build()
            );
            
            // 2. 设置生命周期规则
            if (expirationDays != null && expirationDays > 0) {
                LifecycleRule rule = new LifecycleRule.Builder()
                    .withId("rule_" + bucket)
                    .withExpirationDays(expirationDays)
                    .withStatus(true)  // 启用规则
                    .build();
                    
                LifecycleConfigurationBuilder builder = new LifecycleConfigurationBuilder();
                builder.addRule(rule);
                
                lifecycleManager.setBucketLifecycle(bucket, builder);
            }
            
            // 3. 设置存储桶策略
            if (policyType != null) {
                BucketPolicy policy = PolicyFactory.createPolicy(policyType);
                String policyJson = policy.generatePolicy(bucket);
                
                minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(policyJson)
                        .build()
                );
            }
            
            log.info("存储桶创建成功: bucket={}, region={}, expirationDays={}, policyType={}", 
                bucket, region, expirationDays, policyType);
        }
    }

    /**
     * 设置存储桶生命周期规则
     */
    public void setBucketLifecycle(String bucket, Integer expirationDays) throws Exception {
        ValidationUtils.validateBucket(bucket);
        
        if (expirationDays != null && expirationDays > 0) {
            LifecycleRule rule = new LifecycleRule.Builder()
                .withId("rule_" + bucket)
                .withExpirationDays(expirationDays)
                .withStatus(true)
                .build();
                
            LifecycleConfigurationBuilder builder = new LifecycleConfigurationBuilder();
            builder.addRule(rule);
            
            lifecycleManager.setBucketLifecycle(bucket, builder);
            
            log.info("设置存储桶生命周期成功: bucket={}, expirationDays={}", bucket, expirationDays);
        }
    }

    /**
     * 设置存储桶策略
     */
    public void setBucketPolicy(String bucket, PolicyType policyType) throws Exception {
        ValidationUtils.validateBucket(bucket);
        
        BucketPolicy policy = PolicyFactory.createPolicy(policyType);
        String policyJson = policy.generatePolicy(bucket);
        
        minioClient.setBucketPolicy(
            SetBucketPolicyArgs.builder()
                .bucket(bucket)
                .config(policyJson)
                .build()
        );
        
        log.info("设置存储桶策略成功: bucket={}, policyType={}", bucket, policyType);
    }

    /**获取文件流 */
    public GetObjectResponse getFileStream(String bucket, String object) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);
        
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(object).build());
    }

    /**
     * 获取文件信息
     */
    public StatObjectResponse getFileInfo(String bucket, String object) throws Exception {
        ValidationUtils.validateBucket(bucket);
        ValidationUtils.validateObject(object);
        
        return minioClient.statObject(
            StatObjectArgs.builder()
                .bucket(bucket)
                .object(object)
                .build()
        );
    }
    
    /**
     * 获取文件列表
     * @param bucket 存储桶名称
     * @param prefix 前缀(文件夹路径)
     * @param recursive 是否递归查询子文件夹
     * @return 文件信息列表
     */
    public List<FileItemDTO> listFiles(String bucket, String prefix, boolean recursive) throws Exception {
        ValidationUtils.validateBucket(bucket);
        
        // 处理前缀
        prefix = prefix == null ? "" : prefix.trim();
        if (!prefix.isEmpty() && !prefix.endsWith("/")) {
            prefix = prefix + "/";
        }
        
        log.info("开始获取文件列表: bucket={}, prefix={}, recursive={}", bucket, prefix, recursive);
        
        List<FileItemDTO> result = new ArrayList<>();
        Set<String> directories = new HashSet<>();
        
        try {
            Iterable<Result<Item>> items = minioClient.listObjects(
                ListObjectsArgs.builder()
                    .bucket(bucket)
                    .prefix(prefix)
                    .recursive(recursive)
                    .delimiter(recursive ? "" : "/")
                    .build()
            );

            for (Result<Item> itemResult : items) {
                Item item = itemResult.get();
                String objectName = item.objectName();

                // 跳过当前目录
                if (objectName.equals(prefix)) {
                    continue;
                }

                // 处理文件夹
                if (objectName.endsWith("/")) {
                    String dirName = objectName;
                    if (!prefix.isEmpty()) {
                        dirName = objectName.substring(prefix.length());
                    }
                    dirName = dirName.substring(0, dirName.length() - 1);

                    if (!directories.contains(dirName)) {
                        directories.add(dirName);
                        result.add(FileItemDTO.builder()
                            .name(dirName)
                            .path(objectName)
                            .size(0)
                            .uploadTime(new Date())
                            .directory(true)
                            .fileType("folder")
                            .order(0.0f)
                            .url("/" + bucket + "/" + objectName)
                            .build());
                    }
                    continue;
                }

                // 处理文件
                String fileName = objectName;
                if (!prefix.isEmpty()) {
                    fileName = objectName.substring(prefix.length());
                }

                result.add(FileItemDTO.builder()
                    .name(fileName)
                    .path(objectName)
                    .size((int)item.size())
                    .uploadTime(Date.from(item.lastModified().toInstant()))
                    .directory(false)
                    .etag(item.etag())
                    .fileType(ContentTypeUtils.getContentType(fileName))
                    .order(1.0f)
                    .url("/" + bucket + "/" + objectName)
                    .build());
            }
            
            return result;
            
        } catch (Exception e) {
            log.error("获取文件列表失败: bucket={}, prefix={}, error={}", bucket, prefix, e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 检查存储桶是否存在
     * @param bucketName 存储桶名称
     * @return 是否存在
     */
    public boolean bucketExists(String bucketName) {
        try {
            ValidationUtils.validateBucket(bucketName);
            
            return minioClient.bucketExists(
                BucketExistsArgs.builder()
                    .bucket(bucketName)
                    .build()
            );
        } catch (Exception e) {
            log.error("检查存储桶是否存在失败: bucket={}, error={}", bucketName, e.getMessage());
            return false;
        }
    }

    /**
     * 创建存储桶
     * @param bucketName 存储桶名称
     * @param region 区域(可选)
     * @return 是否创建成功
     */
    public boolean createBucket(String bucketName, String region) {
        try {
            ValidationUtils.validateBucket(bucketName);
            
            // 检查存储桶是否已存在
            if (!bucketExists(bucketName)) {
                // 创建存储桶
                MakeBucketArgs.Builder builder = MakeBucketArgs.builder()
                    .bucket(bucketName);
                    
                // 如果指定了区域，则设置区域
                if (region != null && !region.isEmpty()) {
                    builder.region(region);
                }
                
                minioClient.makeBucket(builder.build());
                
                log.info("创建存储桶成功: bucket={}, region={}", bucketName, region);
                return true;
            }
            
            log.info("存储桶已存在: bucket={}", bucketName);
            return true;
            
        } catch (Exception e) {
            log.error("创建存储桶失败: bucket={}, region={}, error={}", 
                bucketName, region, e.getMessage());
            return false;
        }
    }

    @Data
    @Builder
    public static class FileItemDTO {
        private Long attachmentId;      // 附件ID
        private String name;            // 显示的文件名
        private String path;            // 完整路径
        private Integer size;           // 文件大小
        private Date uploadTime;        // 上传时间
        private boolean directory;      // 是否是目录
        private String etag;            // 文件etag
        private String fileType;        // 文件类型
        private Float order;            // 排序值
        private String url;             // 完整访问URL
    }
}